<template>
  <div class="upload-container">
    <div class="trigger-container" @click="onUpload">
      <input
        class="hidden"
        ref="fileUploader"
        type="file"
        :multiple="multiple"
        :accept="acceptType"
        @change="fileChange"
      />
      <slot></slot>
    </div>
    <div v-if="help" class="file-help">
      {{help}}
    </div>
    <ul class="files-container" v-if="showFiles">
      <li v-for="file in fileList" :key="file.name" class="sspace-vertical">
        <s-icon type="icon-file"/>
        <span class="sspace-horizon">{{file.name}}</span>
      </li>
    </ul>
  </div>
</template>

<script lang='ts'>
import { file } from "@babel/types"
import { PropType, computed, ref, watch, reactive, toRefs } from "vue"
import { Message, SIcon } from "@/components"

type Limit={
    size?:number, // 文件大小  单位M
    maxFiles?:number, // 文件数量
    [index:string]:string|number|undefined
}
enum FILE_STATUS{
    EMPTY=0,
    SUCCESS=1,
    ERROR=2,
    UPLOADING=3
}
// 状态的结构
type State={
    fileData:any[]|object,
    fileStatus:FILE_STATUS,
    fileList:FileList|[],
    fileIndex:number
}
export default {
   name: "s-upload",
   components: { SIcon },
   props: {
      action: String,
      initFile: {
         type: [Array, Object],
         default: null
      },
      accept: {
         type: [String, Array],
         default: "*"
      },
      limit: Object as PropType<Limit>,
      multiple: {
         type: Boolean,
         default: false
      },
      beforeUpload: Function as PropType<(files:FileList)=>boolean>,
      showFiles: {
         type: Boolean,
         default: false
      },
      help: String
   },

   emits: ["onSuccess", "onError"],

   setup (props, context) {
      const fileUploader = ref<null | HTMLInputElement>(null)

      const acceptType = computed(() => {
         if (typeof props.accept !== "string") {
            if (Array.isArray(props.accept)) {
               return props.accept.join()
            } else {
               console.error("accept接收字符串或数组，请输入正确的格式")
            }
         }
         return props.accept
      })

      const state = reactive<State>({
         fileData: props.initFile,
         fileStatus: FILE_STATUS.ERROR,
         fileList: [],
         fileIndex: 0
      })

      // 监听是否有初始文件
      watch(() => props.initFile, (val) => {
         if (val) {
            state.fileStatus = FILE_STATUS.SUCCESS
            state.fileData = val
         }
      })

      const onUpload = (e:Event) => {
         if (fileUploader.value) {
            fileUploader.value.click()
         }
      }

      // 自定义验证
      const customCheck = async (files:FileList) => {
         return new Promise((resolve, reject) => {
            if (props.beforeUpload) {
               const result = props.beforeUpload(files)
               if (typeof result !== "boolean") {
                  reject(new Error("beforeUploadu应该返回一个布尔值"))
               }
               resolve(result)
            } else {
               resolve(true)
            }
         })
      }

      // 文件大小验证
      const sizeCheck = (files:FileList) => {
         return new Promise((resolve, reject) => {
            const { size } = props.limit
            if (size) {
               let index = 0
               while (index < files.length) {
                  const file = files[index]
                  const fileSize = file.size / 1024
                  if (fileSize > size) {
                     const msg = `${file.name}文件大小超出${size}K，请重新调整！`
                     Message.error(msg)
                     reject(new Error(msg))
                  }
                  index++
               }
               resolve(true)
            }
            resolve(true)
         })
      }

      // 文件数量验证
      const lengthCheck = (files:FileList) => {
         return new Promise((resolve, reject) => {
            const { maxFiles } = props.limit
            if (maxFiles) {
               console.log(files.length, maxFiles)
               if (files.length > maxFiles) {
                  const msg = `文件数量不得超过${maxFiles}个`
                  Message.error(msg)
                  reject(new Error(msg))
               }
               resolve(true)
            }
            resolve(true)
         })
      }

      const fileChange = async (e:Event) => {
         const target = e.target as HTMLInputElement
         const files = target.files
         if (files && file.length) {
            // 上传前验证
            try {
               await customCheck(files)
               if (props.limit) {
                  await sizeCheck(files)
                  await lengthCheck(files)
               }
            } catch {
               target.value = ""
            }

            // 本地 不上传到服务器时
            if (!props.action) {
               context.emit("onSuccess", files)
               state.fileList = files
               state.fileStatus = FILE_STATUS.SUCCESS
               target.value = ""
            } else {
               state.fileStatus = FILE_STATUS.UPLOADING
               state.fileList = files
               target.value = ""
               uploadFile(files[state.fileIndex])
            }
         }
      }

      // 上传文件
      const uploadFile = async (file:File) => {
         try {
            const fd = new FormData()
            fd.append("file", file)
            // const data = await $axios.upload(props.action, fd)
            //   if (true) {
            //   await isFinish()
            // } else {
            //   throw new Error(`${file.name}在上传过程中发生错误，上传中止`)
            // }
         } catch (err) {
            state.fileStatus = FILE_STATUS.ERROR
            state.fileList = []
            context.emit("onError")
            console.error(err)
         } finally {
            state.fileIndex = 0
         }
         state.fileStatus = FILE_STATUS.SUCCESS
         context.emit("onSuccess", state.fileList)
         state.fileList = []
      }

      // 遍历文件
      const isFinish = () => {
         return new Promise((resolve, reject) => {
            // 如果有多个文件
            if (props.multiple && state.fileList.length > 1) {
               // 判断当前文件索引和文件列表长度
               if (state.fileIndex < state.fileList.length - 1) {
                  state.fileIndex++
                  uploadFile(state.fileList[state.fileIndex])
               } else {
                  resolve(FILE_STATUS.SUCCESS)
               }
            } else {
               resolve(FILE_STATUS.SUCCESS)
            }
         })
      }

      return {
         acceptType,
         fileChange,
         onUpload,
         fileUploader,
         ...toRefs(state),
         ...toRefs(props)
      }
   }
}
</script>

<style scoped lang="less">
.upload-container{
  display: flex;
  flex-direction: column;
  .file-help{
    color:@help;
    margin: 10px 0;
    font-size: 0.85em;
  }
  .files-container{
    cursor: default;
    position: absolute;
    top: 36px;
  }
}

</style>
